fix!: normalise InvalidArgumentError to server_error on the wire#451
fix!: normalise InvalidArgumentError to server_error on the wire#451dhensby wants to merge 1 commit into
Conversation
6a484fe to
83abd6a
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes a standards-compliance issue where InvalidArgumentError (with non-standard invalid_argument) could leak to OAuth clients during request handling, by coercing it to a ServerError at the handler serialization boundary while preserving the original error as .inner.
Changes:
- Normalize
InvalidArgumentErrortoServerErrorinTokenHandlerandAuthorizeHandlercatch blocks (request-handling path only). - Add integration tests asserting
server_erroris returned on the wire for the reachable runtimeInvalidArgumentErrorcases (token response body + status, authorize redirect query params).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| lib/handlers/token-handler.js | Wraps request-time InvalidArgumentError as ServerError before updating the token error response. |
| lib/handlers/authorize-handler.js | Wraps request-time InvalidArgumentError as ServerError before building the authorization error redirect URI. |
| test/integration/handlers/token-handler_test.js | Adds integration coverage ensuring token endpoint returns server_error/503 and retains the original error as .inner. |
| test/integration/handlers/authorize-handler_test.js | Adds integration coverage ensuring authorize endpoint redirects with error=server_error and retains the original error as .inner. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
While this is non-breaking in terms of the OAuth workflow, it might break existing systems that attempt to catch |
|
I agree, but feel like it should be a major release out of caution for those users. |
83abd6a to
fac13b8
Compare
|
While I agree on that as well (and I think with #399 we are anyway forced to bump major) I wonder about the maintenance of older major versions, especially 4.x for which we were already merging back some newer fixes. While I personally value this a lot, this can get easily out of hand if we have too many major version branches to maintain. |
|
I'm also going to open a PR that supersedes #336 and that will be breaking too, so I think we should think about a new major soon. If we implement the semantic-release PR #335 - then support for older versions falls out of that by creating |
InvalidArgumentError carries the non-standard `invalid_argument` code (defined in neither RFC 6749 §5.2 nor §4.1.2.1) but extends OAuthError, so the token and authorize handler catch blocks let it slip through their "wrap any non-OAuthError as ServerError" guard. As a result, a misbehaving model that returns malformed token data (e.g. a non-Date `accessTokenExpiresAt`) or a falsy authorization code caused the server to emit `error=invalid_argument` with HTTP 500 to the client instead of a standard `server_error`. Treat InvalidArgumentError like any other internal error at the serialisation boundary: normalise it to ServerError before it reaches the client, preserving the original as `.inner`. Construction- and request-validation guards (missing options, model-capability checks, request/response instance checks) are thrown before the try block and are unaffected — they still surface InvalidArgumentError to the integrator as a descriptive developer-facing error. BREAKING CHANGE: when a model returns malformed token data or a falsy authorization code at request time, the OAuth error response now reports `error: server_error` with HTTP status 503 instead of `error: invalid_argument` with HTTP status 500. Setup-time InvalidArgumentErrors thrown to integrator code are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
256e2dd to
1724f63
Compare
What
InvalidArgumentErrorcarries the non-standardinvalid_argumentcode but extendsOAuthError, so the token and authorize handler catch blocks — which only wrap non-OAuthErrorerrors asServerError— let it pass straight through and serialize it to the client. This normalisesInvalidArgumentErrortoServerErrorat the serialisation boundary, so genuine runtime failures surface as a standardserver_errorinstead of leakinginvalid_argument.Resolves #67.
Why
invalid_argumentis defined in none of RFC 6749's error sets (§5.2 token endpoint; §4.1.2.1 / §4.2.2.1 authorization endpoint) nor the IANA registry. Of the ~56InvalidArgumentErrorthrow sites, ~32 are construction/config-time guards thrown straight back to the integrator that never reach a client — for those,invalid_argumentis a useful developer signal and stays as-is. The defect is the small subset thrown during request handling, inside the handlertry, which currently serialize aserror=invalid_argument/ HTTP 500:TokenModel/BearerTokenTypethrowCodeResponseTypethrowsChange
Both handler catch blocks now also coerce
InvalidArgumentErrortoServerError(preserving the original as.inner):Setup-time guards (missing options,
model does not implement X(),request/responseinstance checks) are thrown before thetryblock, so they're unaffected and still surfaceInvalidArgumentErrorto integrator code.Tests
Added two integration tests proving the wire output for the reachable paths is now
server_error/503 (token endpoint) and anerror=server_errorredirect (authorize endpoint), with the originalInvalidArgumentErrorretained as.inner. Full suite green; no existingInvalidArgumentErrorassertion changes (all are on pre-tryconstruction / request-instance paths).BREAKING CHANGE
When a model returns malformed token data or a falsy authorization code at request time, the OAuth error response now reports
error: server_error(HTTP 503) instead oferror: invalid_argument(HTTP 500). Setup-timeInvalidArgumentErrors thrown to integrator code are unchanged.